﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;

namespace eVision.SharpSort
{
    public static class StringFormater
    {
        #region Static
        // static methods (1)

        public static string SimpleFormat(this Object target)
        {
            StringBuilder sb = new StringBuilder();
            Type type = target.GetType();

            sb.AppendLine(type.FullName);
            sb.AppendLine("{");
            foreach (PropertyInfo property in type.GetProperties())
            {
                sb.Append("\t").Append(property.Name).Append(" = ").AppendLine(property.GetValue(target, null) + "");
            }
            sb.AppendLine("}");

            return sb.ToString();
        }
        #endregion
    }

    #region Where
    public class Constants
    {
        #region Static
        // static fields (4)

        public const String andElementMarkup = "And";
        public const String conditionElementMarkup = "Condition";
        public const String orElementMarkup = "Or";
        public const String whereMarkup = "Where";
        #endregion

    }


    public interface IEvaluateExpression
    {
        #region Methods (1)

        bool Evaluate(CodeMemberInfo member);

        #endregion

    }

    [XmlRoot(Constants.andElementMarkup)]
    public class And : Where
    {
        #region Constructors (1)
        public And(XmlElement e) : base(e) { }
        #endregion

    }

    [XmlRoot(Constants.orElementMarkup)]
    public class Or : ParseNode, IEvaluateExpression
    {
        #region Constructors (1)
        public Or(XmlElement e) : base(e) { }
        #endregion

        #region Methods (1)

        public bool Evaluate(CodeMemberInfo info)
        {
            foreach (IEvaluateExpression child in Children)
            {
                if (child.Evaluate(info))
                    return true;
            }
            return false;
        }

        #endregion


    }

    [XmlRoot(Constants.conditionElementMarkup)]
    public class Condition : IEvaluateExpression
    {
        #region Enums (2)
        public enum OperatorEnum { Invalid, Equal, NotEqual };

        public enum PartEnum { Invalid, AccessModifier, EntityType, Name, IsStatic, ReturnType };
        #endregion

        #region Fields (2)
        private Regex compiledValue;

        private String compiledValueOriginal;
        #endregion

        #region Properties (3)
        [XmlAttribute()]
        public OperatorEnum Operator { get; set; }

        [XmlAttribute()]
        public PartEnum Part { get; set; }

        [XmlAttribute()]
        public String Value { get; set; }
        #endregion

        #region Methods (1)

        public bool Evaluate(CodeMemberInfo info)
        {
            if (Part == PartEnum.Invalid)
                throw new ArgumentException("PartOfMember is not defined.");
            if (Operator == OperatorEnum.Invalid)
                throw new ArgumentException("OperatorType is not defined.");
            if (Value == null)
                throw new ArgumentException("Value is not defined.");

            bool satisfies;

            switch (Part)
            {
                case PartEnum.AccessModifier:
                    satisfies = (info.AccessModifier == (CodeMemberInfo.AccessModifierEnum)Enum.Parse(typeof(CodeMemberInfo.AccessModifierEnum), Value));
                    break;
                case PartEnum.IsStatic:
                    satisfies = info.IsStatic == bool.Parse(Value);
                    break;
                case PartEnum.Name:
                    if (compiledValueOriginal != Value)
                        compiledValue = null;
                    if (compiledValue == null)
                    {
                        String regexString = Value;

                        if (!regexString.StartsWith("^"))
                            regexString = "^" + regexString;
                        if (!regexString.EndsWith("$"))
                            regexString = regexString + "$";
                        compiledValue = new Regex(regexString, RegexOptions.Singleline);
                        compiledValueOriginal = Value;
                    }

                    satisfies = compiledValue.IsMatch(info.Name);
                    break;
                case PartEnum.ReturnType:
                    satisfies = info.EntityVariableType == Value;
                    break;
                case PartEnum.EntityType:
                    satisfies = (info.EntityType == (CodeMemberInfo.EntityTypeEnum)Enum.Parse(typeof(CodeMemberInfo.EntityTypeEnum), Value));
                    break;
                default:
                    throw new ArgumentException("Bug");
            }

            if (Operator == OperatorEnum.Equal)
                return satisfies;
            else if (Operator == OperatorEnum.NotEqual)
                return !satisfies;
            else
                throw new ArgumentException("Operator not defined");
        }

        #endregion

    }

    [XmlRoot(Constants.whereMarkup)]
    public class Where : ParseNode, IEvaluateExpression
    {
        #region Constructors (2)
        public Where() : base(null) { }

        public Where(XmlElement e) : base(e) { }
        #endregion

        #region Methods (1)

        public bool Evaluate(CodeMemberInfo info)
        {
            foreach (IEvaluateExpression node in Children)
                if (!node.Evaluate(info))
                {
                    return false;
                }

            return true;
        }

        #endregion

    }

    public abstract class ParseNode
    {
        #region Fields (1)
        [XmlIgnore]
        public IEvaluateExpression[] Children;
        #endregion

        #region Constructors (1)
        public ParseNode(XmlElement element)
        {
            if (element == null)
            {
                Children = new IEvaluateExpression[0];
                return;
            }

            List<IEvaluateExpression> children = new List<IEvaluateExpression>();
            foreach (XmlNode node in element.ChildNodes)
            {
                XmlElement childElem = node as XmlElement;

                if (childElem == null) continue;

                if (ParseInformation.ConstructorByTagName.ContainsKey(childElem.Name))
                {
                    IEvaluateExpression evaluateExpression = (IEvaluateExpression)ParseInformation.ConstructorByTagName[childElem.Name].Invoke(new Object[] { childElem });
                    children.Add(evaluateExpression);
                }
                else
                {
                    //if (!ParseInformation.ConstructorByTagName.ContainsKey(childElem.Name))
                    //    throw new ArgumentOutOfRangeException(childElem.OuterXml);

                    IEvaluateExpression evaluateExpression = (IEvaluateExpression)Serializer.XmlDeserialize(ParseInformation.TypeByTagName[childElem.Name], childElem.OuterXml);
                    children.Add(evaluateExpression);
                }
            }

            Children = children.ToArray();
        }
        #endregion

    }
    #endregion

    #region OrderBy
    [XmlRoot("Parameter")]
    public class OrderParameter
    {
        #region Enums (2)
        public enum DirectionType { Ascending, Descending };

        public enum PartEnum { Invalid, AccessModifier, EntityType, Name, IsStatic, EntityVariableType, NumberOfParameters };
        #endregion

        #region Properties (2)
        [XmlAttribute("Direction")]
        public DirectionType Direction { get; set; }

        [XmlAttribute("Part")]
        public PartEnum Part { get; set; }
        #endregion
    }
    #endregion

    public class ParseInformation
    {
        #region Static
        // static fields (3)

        public static IDictionary<String, ConstructorInfo> ConstructorByTagName;
        static private Type[] ParseConstructorParameters = new Type[] { typeof(XmlElement) };
        public static IDictionary<String, Type> TypeByTagName;
        #endregion

        #region Constructors (1)
        static ParseInformation()
        {
            ConstructorByTagName = new Dictionary<String, ConstructorInfo>();
            TypeByTagName = new Dictionary<String, Type>();

            Assembly executingAssembly = Assembly.GetExecutingAssembly();
            String thisNamespace = typeof(ParseInformation).Namespace;

            foreach (Type type in executingAssembly.GetTypes())
            {
                if (type.Namespace == thisNamespace)
                {
                    XmlRootAttribute[] xmlRoot = type.GetCustomAttributes(typeof(XmlRootAttribute), false).Cast<XmlRootAttribute>().ToArray();

                    if (xmlRoot.Length > 0)
                    {
                        ConstructorInfo constructorInfo = type.GetConstructor(ParseConstructorParameters);
                        if (constructorInfo != null)
                            ConstructorByTagName[xmlRoot[0].ElementName] = constructorInfo;

                        TypeByTagName[xmlRoot[0].ElementName] = type;
                    }
                }
            }
        }
        #endregion

    }
}
